﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

using Azure.Configuration;
using Azure.Logger;

namespace Azure.Core
{
    internal class AzParseContent
    {
        public String HTML { get; set; }
        public String URI { get; set; }
    }

    internal class AzNewTask
    {
        public AzParser WhichParser { get; set; }
        public String taskUri { get; set; }
    }

    internal class AzRobot
    {
        public delegate void NewTaskFoundDelegate(AzRobot sender, List<AzNewTask> taskContext);
        public delegate void RobotCompleteDelegate(AzRobot sender);

        private String _name = String.Empty;
        private AzRobotConfigItem _config = null;
        private AzParserFactory _parserFactory = null;
        private bool _isFinished = false;
        private Dictionary<String, ISerializer> _serializerDict = null;
        //所有正在处理的Parser,如果数量为0，则说明这个Robot已经完成了所有的工作
        //Key = Parser, Value = URI
        private Dictionary<AzParser, String> _workingParserSet = null;

        public String Name { get { return _name; } }

        //事件
        public event NewTaskFoundDelegate NewTaskFoundEvent;
        public event RobotCompleteDelegate RobotCompleteEvent;

        public AzRobot(AzRobotConfigItem config)
        {
            _config = config;
            _name = config.Name;
            _serializerDict = new Dictionary<String, ISerializer>();
            //创建Serializers
            if (!CreateSerializer())
            {
                throw new Exception("机器人" + _name + "没有一个合法的序列化器");
            }

            _workingParserSet = new Dictionary<AzParser, String>();
            _parserFactory = new AzParserFactory(config);
        }

        public bool IsFinished()
        {
            return _isFinished;
        }

        public void Begin()
        {
            //手动创建第一个task
            AzNewTask newTask = new AzNewTask();
            newTask.taskUri = _config.StartCrawlingUri;
            AzParser parser = _parserFactory.CreateParser(_config.StartParserName);

            if (parser == null)
            {
                AzLogger.Error("机器人" + this._name + "创建第一个任务失败：没有成功找到parser:" + _config.StartParserName);
                _isFinished = true;

                //初始任务就失败，肯定是结束了
                if (this.RobotCompleteEvent != null)
                    RobotCompleteEvent(this);

                return;
            }
            else 
            {
                //注册事件
                parser.NewDataBlockFoundEvent += this.OnNewDataBlockFoundEvent;
                parser.NewTaskFoundEvent += this.OnNewTaskFoundEvent;
                parser.ParseCompleteEvent += this.OnParseCompleteEvent;
                parser.RaiseErrorHandlingEvent += this.OnRaiseErrorHandlingEvent;

                //加入工作集,这里不用担心线程安全
                this._workingParserSet.Add(parser, newTask.taskUri);
                newTask.WhichParser = parser;

                List<AzNewTask> param = new List<AzNewTask>();
                param.Add(newTask);

                NewTaskFoundEvent(this, param);
            }
        }

        public void Execute(AzNewTask task)
        {
            //创建Loader并下载
            AzPageLoader loader = null;
            try
            {
                loader = new AzPageLoader(task.taskUri, _config.Loader.MaxRetryCount, _config.Loader.TimeOutDelay);
            }
            catch (Exception ex)
            {
                AzLogger.Error("任务" + task.taskUri + "在创建loader时失败，任务终止", ex);
                //出错了也要通知啊
                OnParseCompleteEvent(task.WhichParser);
                return;
            }

            try
            {
                //下载
                AzLogger.PerfStart(task.taskUri);
                String html = loader.Load();
                AzLogger.PerfEnd();

                if (html != String.Empty)
                {
                    AzParseContent content = new AzParseContent();
                    content.HTML = html;
                    content.URI = task.taskUri;

                    //开始分析
                    AzParser parser = task.WhichParser;
                    parser.BeginParse(content);
                }
                else
                {
                    throw new Exception("从页面" + task.taskUri + "没有成功下载到html文本，html为空");
                }
            }
            catch (Exception ex)
            {
                AzLogger.Error("机器人" + this._name + "在处理任务" + task.taskUri + "时发生了未知的错误", ex);
                OnParseCompleteEvent(task.WhichParser);
            }
        }

        //注意线程安全
        private void OnParseCompleteEvent(AzParser sender)
        {
            lock (this._workingParserSet)
            {
                this._workingParserSet.Remove(sender);

                sender.NewDataBlockFoundEvent -= this.OnNewDataBlockFoundEvent;
                sender.NewTaskFoundEvent -= this.OnNewTaskFoundEvent;
                sender.ParseCompleteEvent -= this.OnParseCompleteEvent;
                sender.RaiseErrorHandlingEvent -= this.OnRaiseErrorHandlingEvent;

                //所有parser都完成了工作
                if (this._workingParserSet.Count == 0)
                {
                    this._isFinished = true;
                    //通知所有订阅了完成事件的对象
                    RobotCompleteEvent(this);
                }
            }
        }

        //注意线程安全
        private void OnNewDataBlockFoundEvent(AzParser sender, List<AzNewDataBlock> dataList)
        {
            //异步方式，让Serializer来序列化新数据
            foreach (ISerializer serializer in this._serializerDict.Values)
            {
                serializer.Serialize(dataList);
            }
        }

        //注意线程安全
        private void OnRaiseErrorHandlingEvent(AzParser sender, AzErrorHandling errorHandling)
        {
            switch (errorHandling.Operation)
            {
                case ErrorHandlingOperation.CONTINUE:
                    break;
                case ErrorHandlingOperation.STOPPARSE:
                    //TODO:
                    break;
                case ErrorHandlingOperation.REDIRECT:
                    //解析param,格式为parser:uri
                    String[] parameters = errorHandling.Param.Split(new char[]{':'});
                    if (parameters.Count() != 2)
                    {
                        AzLogger.Error(this._name + ":" + sender.Name + "ErrorHandling的参数格式不正确");
                        break;
                    }

                    //创建新任务
                    AzNewTaskContent content = new AzNewTaskContent();
                    content.WhichParser = parameters[0];
                    content.URI = parameters[1];
                    
                    AzLogger.Info(this._name + "重定向任务到" + content.URI + "新的Parser为" + content.WhichParser + "原来的Parser为" + sender.Name);
                    List<AzNewTaskContent> param = new List<AzNewTaskContent>();
                    param.Add(content);

                    //代码重用，个人认为语义上不太正确
                    OnNewTaskFoundEvent(sender, param);
                    break;
            }
        }

        //注意线程安全
        private void OnNewTaskFoundEvent(AzParser sender, List<AzNewTaskContent> newTaskList)
        {
            List<AzNewTask> param = new List<AzNewTask>();
            foreach (AzNewTaskContent task in newTaskList)
            {
                AzNewTask newTask = new AzNewTask();
                newTask.taskUri = task.URI;
                AzParser parser = _parserFactory.CreateParser(task.WhichParser);

                if (parser == null)
                {
                    AzLogger.Error("机器人" + this._name + "创建任务失败：没有成功找到parser:" + task.WhichParser);
                }
                else
                {
                    //注册事件
                    parser.NewDataBlockFoundEvent += this.OnNewDataBlockFoundEvent;
                    parser.NewTaskFoundEvent += this.OnNewTaskFoundEvent;
                    parser.ParseCompleteEvent += this.OnParseCompleteEvent;
                    parser.RaiseErrorHandlingEvent += this.OnRaiseErrorHandlingEvent;

                    //加入工作集
                    lock (this._workingParserSet)
                    {
                        this._workingParserSet.Add(parser, newTask.taskUri);
                    }

                    newTask.WhichParser = parser;

                    param.Add(newTask);
                }
            }//end foreach (AzNewTaskContent task in newTaskList)

            //通知CoreEngine,让CoreEngine来安排运行这个任务     
            NewTaskFoundEvent(this, param);
        }

        private bool CreateSerializer()
        {
            bool isAtLeastOneValidSerializer = false;
            foreach (AzSerializerConfigItem config in this._config.SerializeList)
            {
                try
                {
                    Assembly assembly = Assembly.Load(config.AssemblyName);
                    Type t = assembly.GetType(config.FullClassName);

                    //检查type是否继承了ISerializer接口
                    if (t.GetInterface("ISerializer") == null)
                    {
                        AzLogger.Warn("程序集" + config.AssemblyName + "中的类型" + config.FullClassName + "没有实现接口ISerializer");
                        continue;
                    }

                    //如果继承了，则根据配置文件中的构造函数参数，来调用相应的构造函数
                    Object[] args = new Object[config.CtorArgDict.Count];
                    Type[] argsType = new Type[config.CtorArgDict.Count];

                    int index = 0;
                    foreach (String key in config.CtorArgDict.Keys)
                    {
                        args[index] = config.CtorArgDict[key];
                        argsType[index] = args[index].GetType();
                        index++;
                    }

                    ConstructorInfo ctor = t.GetConstructor(argsType);
                    //创建，初始化属性，并加入到字典中
                    ISerializer serializer = ctor.Invoke(args) as ISerializer;

                    foreach (String properyName in config.PropertyDict.Keys)
                    {
                        PropertyInfo pi = t.GetProperty(properyName);
                        pi.SetValue(serializer, config.PropertyDict[properyName], null);
                    }

                    this._serializerDict.Add(config.Name, serializer);
                }
                catch (Exception ex)
                {
                    AzLogger.Warn("反射创建程序集" + config.AssemblyName + "中的类型" + config.FullClassName + "时失败", ex);
                }
                
                isAtLeastOneValidSerializer = true;
            }

            return isAtLeastOneValidSerializer;
        }

        //在Robot初始化时，根据配置文件，创建对应多个Parser做为样板。因为这些Parser
        //都是初始状态（主要是ErrorHandlingIndex为0）,所以在以后抓取过程中，一旦有新页面
        //需要状态Parser，则可以用这些样板来克隆出新的实例来。
        private class AzParserFactory
        {
            private AzRobotConfigItem _config = null;
            //Key = parser name
            //Value = parser object
            private Dictionary<String, AzParser> _parserDict = null;

            public AzParserFactory(AzRobotConfigItem config)
            {
                _config = config;
                Init();
            }

            public AzParser CreateParser(String parserName)
            {
                //如果字典中没有，则说明某个Uri关联的parser不对
                //检查配置文件是否写正确了
                if (!_parserDict.Keys.Contains(parserName))
                {
                    return null;
                }
                else
                {
                    return _parserDict[parserName].Clone();
                }
            }

            private void Init()
            {
                _parserDict = new Dictionary<string, AzParser>();

                //创建Parser
                foreach (AzParserConfigItem parserConfig in _config.ParserDict.Values)
                {
                    AzParser parser = new AzParser(parserConfig.Name);

                    //创建UriTarget
                    foreach (AzUriTargetConfigItem uriTargetConfig in parserConfig.UriTargetDict.Values)
                    {
                        //创建Formula
                        AzFormula formula = new AzFormula(uriTargetConfig.Formula.Regex,
                                                          uriTargetConfig.Formula.Prefix,
                                                          uriTargetConfig.Formula.UniqueMatch);
                        foreach (String groupName in uriTargetConfig.Formula.GroupNameList)
                        {
                            formula.AddGroupName(groupName);
                        }

                        AzUriTarget uriTarget =
                            new AzUriTarget(uriTargetConfig.Name, uriTargetConfig.ParserName, formula);

                        //创建ErrorHandling
                        foreach (AzErrorHandlingConfigItem errorHandlingConfig in uriTargetConfig.ErrorHandlingList)
                        {
                            AzErrorHandling errorHandling =
                                new AzErrorHandling(errorHandlingConfig.Operation, errorHandlingConfig.Param);

                            uriTarget.AddErrorHandling(errorHandling);
                        }//end 创建ErrorHandling

                        parser.AddUriTarget(uriTarget);
                    }//end 创建UriTarget

                    //创建BlockTarget
                    foreach (AzBlockTargetConfigItem blkTargetConfig in parserConfig.BlockTargetDict.Values)
                    {
                        AzBlockTarget blkTarget =
                            new AzBlockTarget(blkTargetConfig.Name, blkTargetConfig.TargetDict.Count);

                        //创建Target
                        foreach (AzTargetConfigItem targetConfig in blkTargetConfig.TargetDict.Values)
                        {
                            //创建Formula
                            AzFormula formula = new AzFormula(targetConfig.Formula.Regex,
                                                              targetConfig.Formula.Prefix,
                                                              targetConfig.Formula.UniqueMatch);
                            foreach (String groupName in targetConfig.Formula.GroupNameList)
                            {
                                formula.AddGroupName(groupName);
                            }
                            AzTarget target = new AzTarget(targetConfig.Name,
                                                            targetConfig.Type,
                                                            targetConfig.DefaultValue,
                                                            formula);

                            blkTarget.AddTarget(target);
                        }//end 创建Target

                        parser.AddBlockTarget(blkTarget);
                    }//end 创建BlockTarget

                    _parserDict.Add(parser.Name, parser);
                }//end 创建Parser
            }
        }

    }
}
